/* 
 * File:   State.cpp
 * Author: chadijir
 * 
 * Created on September 26, 2010, 1:42 PM
 */

#include "State.h"
#include <iomanip>
#include "settings.h"

/**
 * Sets up static fields
 */
State::State() {
    cost = 0;
    nextNode = 0;
    size = 0;
}

/**
 * Copies orig to a new instance, copies array as well
 */
State::State(const State& orig) {
    graph = orig.graph;
    size = orig.size;
    cost = orig.cost;
    nextNode = orig.nextNode;
    array = new bool[size];
    for (unsigned int i = 0; i < size; i++) {
        array[i] = orig.array[i];
    }
}

/**
 * Deletes array
 */
State::~State() {
    delete [] array;
}

/**
 * Initializes array with graph.nodeCount, stores graph pointer
 * @param gGraph
 */
void State::init(Graph* gGraph) {
    graph = gGraph;
    size = graph->getNodeCount();
    array = new bool[size];
    for (unsigned int i = 0; i < size; i++) {
        array[i] = 0;
    }
}

/**
 *
 * @return cost
 */
unsigned int State::getCost() const {
    return cost;
}

/**
 * Counts cost by walking through array and using edge weights from graph
 */
void State::countCost() {
    unsigned int i, j;
    cost = 0;
    for (i = 0; i < size; i++) {
        if (array[i] == 1) { // in Y set
            for (j = 0; j < size; j++) { // walk through edges
                if (graph->getValue(i, j) > 0 && array[j] == 0) { // edge exists and node in X set
                    cost += graph->getValue(i, j);
                }
            }
        }
    }

}

/**
 * Sets next node. Next node is used to determine where to expand a particular
 * state
 * @param i index of next node
 */
void State::setNextNode(unsigned int i) {
    if (i < size) nextNode = i;
}

/**
 *
 * @return index of next node
 */
unsigned int State::getNextNode() {
    return nextNode;
}

/**
 * Serializes state into array of chars. First n numbers are values of n nodes.
 * Last number in buffer represents nextNode value. init method has to be called
 * before serialization.
 * @param buffer - where to serialize
 * @param bufferSize - max size of buffer
 * @return size of filled portion, typically number of nodes + 1.
 */
int State::serialize(unsigned char* buffer, const int bufferSize) {
    if(size + 1 > (unsigned int)bufferSize) {return -1;}
    unsigned int i = 0;
    for(i = 0; i < size; i++) {
        buffer[i] = (unsigned char)array[i];
    }

    buffer[size] = nextNode;
    return size+1;
}

/**
 * Deserializes state from buffer of size bufferSize. No new memory is allocated.
 * init method has to be called before deserialization.
 * @param buffer
 * @param bufferSize
 * @return Should be always zero.
 */
int State::deserialize(unsigned char* buffer, const int bufferSize) {

    int i;
    for(i = 0; i < bufferSize-1; i++) {
        array[i] = (bool)buffer[i];
    }
    nextNode = (unsigned int)buffer[bufferSize-1];

    return 0;
}

/**
 * Helper method used to display fancy output which contains both sets of nodes,
 * set of connecting edges and a total cost.
 * @param out
 * @param state
 * @return
 */
ostream & operator<<(ostream &out, const State &state) {

    unsigned int i, j;
    bool printed = false;

#ifdef SHORT_OUTPUT
    cout << state.array << ':';
    for (i = 0; i < state.size; i++) {
        out << state.array[i] << ' ';
    }
    out <<  '|' << state.cost << '|' << state.nextNode << endl;
    return out;
#endif

    out << "Solution" << endl;
    out << setw(40) << setfill('=') << ' ' << endl;

    out << "X set: ";
    for (i = 0; i < state.size; i++) {
        if (state.array[i] == 1) {
            if (printed) out << ", ";
            out << i;
            printed = true;
        }
    }
    printed = false;
    out << endl << "Y set: ";
    for (i = 0; i < state.size; i++) {
        if (state.array[i] == 0) {
            if (printed) out << ", ";
            out << i;
            printed = true;
        }
    }
    out << endl;


    out << "Connecting edges: ";
    for (i = 0; i < state.size; i++) {
        if (state.array[i] == 1) { // in Y set
            for (j = 0; j < state.size; j++) { // walk through edges
                if (state.graph->getValue(i, j) > 0 && state.array[j] == 0) { // edge exists and node in X set
                    cout << '[' << i << ',' << j << ']';
                }
            }
        }
    }
    cout << endl;

    out << "Total cost: " << state.cost << endl;
    out << endl;

    return out;
}

/**
 * Copies State instance
 * @param aState
 * @return
 */
State& State::operator=(const State& aState) {
    if (this != &aState) {
        delete [] array;
        init(aState.graph);
        cost = aState.cost;
        nextNode = aState.nextNode;
        for (unsigned int i = 0; i < size; i++) {
            array[i] = aState.array[i];
        }
    }
    return *this;
}

/**
 * Shorthand for accessing underlying array of bools
 * @param i index
 * @return value on index
 */
bool & State::operator [](int i) {
    return array[i];
}

unsigned int State::getSize() const {
    return size;
}